package github.sf.mod.matcher;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

/**
 * 使用DoubleArrayTrie的前缀匹配工具类，
 *
 * @author zhoup
 */
public class PrefixMatcher {
    private final AhoCorasickDoubleArrayTrie<Integer> doubleArrayTrie = new AhoCorasickDoubleArrayTrie<>();

    private PrefixMatcher(List<String> candidatePrefixes) {
        buildDoubleArrayTrie(candidatePrefixes);
    }

    private void buildDoubleArrayTrie(List<String> candidatePrefixes) {
        Objects.requireNonNull(candidatePrefixes);
        HashMap<String, Integer> wordMap = candidatePrefixes.stream()
                .reduce(new HashMap<>(), (map, word) -> {
                    map.put(word, 1);
                    return map;
                }, (a, b) -> null);
        doubleArrayTrie.build(wordMap);
    }

    /**
     * 重新构建匹配树
     *
     * @param candidatePrefixes 候选前缀
     */
    public void rebuild(List<String> candidatePrefixes) {
        buildDoubleArrayTrie(candidatePrefixes);
    }

    /**
     * 构建前缀匹配树
     *
     * @param candidatePrefixes 候选的前缀词
     */
    public static PrefixMatcher build(List<String> candidatePrefixes) {
        return new PrefixMatcher(candidatePrefixes);
    }

    /**
     * 获取匹配到的所有值，这是doubleArrayTrie的常用功能
     */
    public List<String> getPrefixes(String rowText) {
        List<String> prefixes = new ArrayList<>();
        if (doubleArrayTrie.size() <= 0) {
            return prefixes;
        }
        List<AhoCorasickDoubleArrayTrie.Hit<Integer>> hits = doubleArrayTrie.parseText(rowText);
        for (AhoCorasickDoubleArrayTrie.Hit<Integer> hit : hits) {
            int begin = hit.begin;
            int end = hit.end;
            if (begin != end && begin == 0) {
                prefixes.add(rowText.substring(begin, end));
            }
        }
        return prefixes;
    }

    /**
     * 判断两个字符串在候选前缀中是否有相同的前缀（即前缀有范围限定）
     * 该方法相比于使用startWith配上list的contain的方法有速度的提升
     */
    public boolean hasSamePrefix(String row1, String row2) {
        if (doubleArrayTrie.size() <= 0) {
            return false;
        }
        List<AhoCorasickDoubleArrayTrie.Hit<Integer>> hit1s = doubleArrayTrie.parseText(row1);
        List<AhoCorasickDoubleArrayTrie.Hit<Integer>> hit2s = doubleArrayTrie.parseText(row2);
        boolean[] endIndex = new boolean[row1.length()];
        for (AhoCorasickDoubleArrayTrie.Hit<Integer> hit1 : hit1s) {
            if (hit1.begin == 0 && hit1.end != hit1.begin) {
                endIndex[hit1.end] = true;
            }
        }
        for (AhoCorasickDoubleArrayTrie.Hit<Integer> hit2 : hit2s) {
            if (hit2.begin == 0 && hit2.end != hit2.begin && hit2.end < endIndex.length) {
                if (endIndex[hit2.end]) {
                    return row1.substring(0, hit2.end).equals(row2.substring(0, hit2.end));
                }
            }
        }
        return false;
    }

    /**
     * 判断两个字符串是否有相同的前缀，没有范围限定
     */
    public static boolean isSamePrefix(String s1, String s2, String prefix) {
        return hasPrefix(s1, prefix) && hasPrefix(s2, prefix);
    }

    public static boolean hasPrefix(String original, String prefix) {
        if (prefix.length() > original.length()) {
            return false;
        }
        return original.startsWith(prefix);
    }
}
